前面的 socket.io
使用原生 JS 來寫 Demo,這邊就試著改用框架來處理,不過畢竟目前還是 Demo 練習性質,所以就直接用 CDN 引入,不使用 cli,至於版本的部分,暫時先用 2.6 版。
先用 script 引入 cdn,再使用 app 去包,每一則訊息的渲染則是透過 v-for
放在 li
標籤上,省了 innerHTML
的書寫。
input
透過 v-model
雙向綁定輸入的值,button
不用透過 DOM 操作,可以直接使用 click 事件來觸發 function。
client / index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.14"></script>
</head>
<body>
<div id="app">
<input type="text" class="input-message" v-model="message" />
<button type="button" class="send-btn" @click="sendMessage()">
Send Message
</button>
<ul class="message-wrapper">
<li v-for="(item, index) in list" :key="index + item">{{ item }}</li>
</ul>
</div>
<script src="./client.js"></script>
</body>
</html>
把原本的 DOM 操作通通拔掉,也不需要額外寫 addEventListener()
。
list
是用來存放訊息的陣列,所以當使用者送出輸入的訊息時,列表即會更新變化,在 sendMessage()
觸發,但對其他使用來說,他必須等到 server-side 轉發訊息回來,再透過 onMessage()
監聽的方式,觸發列表變化,這是流程上兩者的差異。
client / client.js
const app = new Vue({
el: '#app',
data: {
list: [],
message: '',
ws: {},
},
mounted() {
this.ws = new WebSocket('ws://127.0.0.1:3000');
this.ws.onopen = this.onOpen;
this.ws.onmessage = this.onMessage;
this.ws.onclose = this.onClose;
this.ws.onerror = this.onError;
},
methods: {
onOpen() {
console.log(`open : ${this.ws.readyState}`);
},
onMessage(event) {
this.list.push(event.data);
},
onClose() {
console.log(`close : ${this.ws.readyState}`);
},
onError() {
console.log(`error : ${this.ws.readyState}`);
},
sendMessage() {
this.list.push(this.message);
this.ws.send(this.message);
this.message = '';
},
},
});
server-side 在轉發訊息時,需要先確認兩件事,因為廣播事件是傳遞給目前線上的所有使用者,所以第一個需要先過濾掉訊息發送者。舉例來說:
A, B 兩個玩家同時登入在線上,A 玩家送出訊息 Hello all!
,自然系統不需要對 A 玩家發出這則訊息,僅需要通知 B 玩家,反之,B 玩家送出訊息時也是同理。
第二個則是確認該玩家是否斷線,所以透過 readyState 的值來檢查是否符合 WebSocket 保持連線的值,確認目前的玩家列表中,那些人仍維持上線狀態,並只針對這些人發送訊息。
server / server.js
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 3000 });
wss.on('connection', function connection(ws) {
ws.on('message', function (message) {
const bufferMessage = Buffer.from(message).toString();
wss.clients.forEach((client) => {
if (ws !== client && client.readyState === WebSocket.OPEN) {
client.send(bufferMessage);
}
});
});
});